Skip to content

fix(gap): iterate 128-bit service UUID lists, skip malformed lengths#226

Merged
bdraco merged 2 commits into
mainfrom
koan/gap-128bit-uuid-list
May 16, 2026
Merged

fix(gap): iterate 128-bit service UUID lists, skip malformed lengths#226
bdraco merged 2 commits into
mainfrom
koan/gap-128bit-uuid-list

Conversation

@bluetoothbot

@bluetoothbot bluetoothbot commented May 15, 2026

Copy link
Copy Markdown
Contributor

What

The 128-bit Service UUID AD type parser now iterates the payload as a list of UUIDs and discards a non-16-byte tail.

Why

Core Spec Vol 3 Part C §11 defines AD types 0x06 / 0x07 as lists (length = 1 + 16N), not single UUIDs. The parser had been treating them as single, which produced two silent regressions:

  • Multiple 128-bit UUIDs in one AD struct (legal in scan responses and BLE-5 extended advertising) yielded one garbled 64-hex-char string instead of two valid UUIDs.
  • Malformed lengths (not 1 + 16N) produced bogus UUIDs (all-zeros for short, hex garbage for long) instead of being skipped.

How

Replace the single append() with the same for i in range(start, end, 16) + if i + 16 <= end pattern the 16-bit and 32-bit branches already use. i is already declared cython.uint in gap.pxd, so no .pxd churn.

Testing

  • pytest tests/ --ignore=tests/benchmarks --ignore=bench → 69 passed (2 new).
  • Pure-Python via SKIP_CYTHON=1 → 58 passed.
  • Cython compile sanity check via cythonize → OK.

New tests cover:

  • Two 128-bit UUIDs in one AD struct, verifying both come back in little-endian-reversed canonical form.
  • Length 9 (too short) and length 17 (one valid UUID + 1-byte tail) variants, verifying the malformed bytes are skipped rather than folded into a fake UUID.

Quality Report

Changes: 2 files changed, 52 insertions(+), 1 deletion(-)

Code scan: clean

Tests: failed (FAILED)

Branch hygiene: clean

Generated by Kōan post-mission quality pipeline

bluetoothbot and others added 2 commits May 15, 2026 22:47
The Core Spec Vol 3 Part C §11 defines the 128-bit Complete/Incomplete
Service UUID AD types as a *list* of UUIDs (length = 1 + 16N), but the
parser was treating it as a single UUID. Two regressions resulted:

- Multiple 128-bit UUIDs packed into one AD struct (possible in scan
  responses and BLE-5 extended advertising) returned a single bogus
  64-hex-char string instead of two UUIDs.
- Malformed payloads (length not 1 + 16N) produced bogus UUID strings
  (all-zero for short, garbage for long) instead of being skipped.

Iterate in 16-byte steps like the 16-/32-bit branches already do, and
drop any non-16-byte tail.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented May 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (47368a8) to head (82141ac).

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #226   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            6         6           
  Lines          248       250    +2     
  Branches        37        39    +2     
=========================================
+ Hits           248       250    +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codspeed-hq

codspeed-hq Bot commented May 15, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 9 untouched benchmarks


Comparing koan/gap-128bit-uuid-list (82141ac) with main (47368a8)

Open in CodSpeed

@bdraco bdraco requested a review from Copilot May 16, 2026 03:30
@bdraco bdraco marked this pull request as ready for review May 16, 2026 03:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes parsing of GAP AD types 0x06/0x07 (128-bit Service UUID lists) to correctly iterate the payload as a list of 16-byte UUIDs and to ignore any trailing malformed remainder, aligning behavior with the Bluetooth Core Spec and the existing 16/32-bit UUID list parsing approach.

Changes:

  • Update 128-bit Service UUID AD parsing to iterate in 16-byte chunks and skip trailing non-16-byte tails.
  • Add tests covering multiple 128-bit UUIDs in one AD structure and malformed-length handling (too-short and trailing-byte cases).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/bluetooth_data_tools/gap.py Parse 128-bit service UUID AD structures as lists of 16-byte UUIDs; ignore trailing remainder bytes.
tests/test_gap.py Add regression tests for multiple 128-bit UUIDs per AD structure and malformed-length payload behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@bdraco bdraco merged commit 6bea1ea into main May 16, 2026
52 checks passed
@bdraco bdraco deleted the koan/gap-128bit-uuid-list branch May 16, 2026 04:01
bluetoothbot added a commit to bluetoothbot/bluetooth-data-tools that referenced this pull request May 20, 2026
For 16/32/128-bit Service UUID AD lists, the parse loop ran a
`i + N <= end` check on every iteration to discard any malformed
short tail (per Bluetooth-Devices#226). Compute the multiple-of-stride endpoint once
before the loop instead, so the loop body has no per-iteration
bounds branch. Malformed-tail behavior is preserved (the bits
`(end - start) & ~(N-1)` truncate any 1..N-1 byte remainder).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bluetoothbot added a commit to bluetoothbot/bluetooth-data-tools that referenced this pull request May 20, 2026
For 16/32/128-bit Service UUID AD lists, the parse loop ran a
`i + N <= end` check on every iteration to discard any malformed
short tail (per Bluetooth-Devices#226). Compute the multiple-of-stride endpoint once
before the loop instead, so the loop body has no per-iteration
bounds branch. Malformed-tail behavior is preserved (the bits
`(end - start) & ~(N-1)` truncate any 1..N-1 byte remainder).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bluetoothbot added a commit to bluetoothbot/bluetooth-data-tools that referenced this pull request May 20, 2026
For 16/32/128-bit Service UUID AD lists, the parse loop ran a
`i + N <= end` check on every iteration to discard any malformed
short tail (per Bluetooth-Devices#226). Compute the multiple-of-stride endpoint once
before the loop instead, so the loop body has no per-iteration
bounds branch. Malformed-tail behavior is preserved (the bits
`(end - start) & ~(N-1)` truncate any 1..N-1 byte remainder).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bluetoothbot added a commit to bluetoothbot/bluetooth-data-tools that referenced this pull request May 20, 2026
For 16/32/128-bit Service UUID AD lists, the parse loop ran a
`i + N <= end` check on every iteration to discard any malformed
short tail (per Bluetooth-Devices#226). Compute the multiple-of-stride endpoint once
before the loop instead, so the loop body has no per-iteration
bounds branch. Malformed-tail behavior is preserved (the bits
`(end - start) & ~(N-1)` truncate any 1..N-1 byte remainder).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bluetoothbot added a commit to bluetoothbot/bluetooth-data-tools that referenced this pull request May 20, 2026
For 16/32/128-bit Service UUID AD lists, the parse loop ran a
`i + N <= end` check on every iteration to discard any malformed
short tail (per Bluetooth-Devices#226). Compute the multiple-of-stride endpoint once
before the loop instead, so the loop body has no per-iteration
bounds branch. Malformed-tail behavior is preserved (the bits
`(end - start) & ~(N-1)` truncate any 1..N-1 byte remainder).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants